home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 365_01 / cut.c < prev    next >
C/C++ Source or Header  |  1992-04-04  |  14KB  |  701 lines

  1. /* cut.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains function which manipulate the cut buffers. */
  12.  
  13. #include "config.h"
  14. #include "vi.h"
  15. #if TURBOC
  16. #include <process.h>        /* needed for getpid */
  17. #endif
  18. #if TOS
  19. #include <osbind.h>
  20. #define    rename(a,b)    Frename(0,a,b)
  21. #endif
  22.  
  23. # define NANONS    9    /* number of anonymous buffers */
  24.  
  25. static struct cutbuf
  26. {
  27.     short    *phys;    /* pointer to an array of #s of BLKs containing text */
  28.     int    nblks;    /* number of blocks in phys[] array */
  29.     int    start;    /* offset into first block of start of cut */
  30.     int    end;    /* offset into last block of end of cut */
  31.     int    tmpnum;    /* ID number of the temp file */
  32.     char    lnmode;    /* boolean: line-mode cut? (as opposed to char-mode) */
  33. }
  34.     named[27],    /* cut buffers "a through "z and ". */
  35.     anon[NANONS];    /* anonymous cut buffers */
  36.  
  37. static char    cbname;    /* name chosen for next cut/paste operation */
  38. static char    dotcb;    /* cut buffer to use if "doingdot" is set */
  39.  
  40.  
  41. #ifndef NO_RECYCLE
  42. /* This function builds a list of all blocks needed in the current tmp file
  43.  * for the contents of cut buffers.
  44.  * !!! WARNING: if you have more than ~450000 bytes of text in all of the
  45.  * cut buffers, then this will fail disastrously, because buffer overflow
  46.  * is *not* allowed for.
  47.  */
  48. int cutneeds(need)
  49.     BLK        *need;    /* this is where we deposit the list */
  50. {
  51.     struct cutbuf    *cb;    /* used to count through cut buffers */
  52.     int        i;    /* used to count through blocks of a cut buffer */
  53.     int        n;    /* total number of blocks in list */
  54.  
  55.     n = 0;
  56.  
  57.     /* first the named buffers... */
  58.     for (cb = named; cb < &named[27]; cb++)
  59.     {
  60.         if (cb->tmpnum != tmpnum)
  61.             continue;
  62.  
  63.         for (i = cb->nblks; i-- > 0; )
  64.         {
  65.             need->n[n++] = cb->phys[i];
  66.         }
  67.     }
  68.  
  69.     /* then the anonymous buffers */
  70.     for (cb = anon; cb < &anon[NANONS]; cb++)
  71.     {
  72.         if (cb->tmpnum != tmpnum)
  73.             continue;
  74.  
  75.         for (i = cb->nblks; i-- > 0; )
  76.         {
  77.             need->n[n++] = cb->phys[i];
  78.         }
  79.     }
  80.  
  81.     /* return the length of the list */
  82.     return n;
  83. }
  84. #endif
  85.  
  86. static void maybezap(num)
  87.     int    num;    /* the tmpnum of the temporary file to [maybe] delete */
  88. {
  89.     char    cutfname[80];
  90.     int    i;
  91.  
  92.     /* if this is the current tmp file, then we'd better keep it! */
  93.     if (tmpfd >= 0 && num == tmpnum)
  94.     {
  95.         return;
  96.     }
  97.  
  98.     /* see if anybody else needs this tmp file */
  99.     for (i = 27; --i >= 0; )
  100.     {
  101.         if (named[i].nblks > 0 && named[i].tmpnum == num)
  102.         {
  103.             break;
  104.         }
  105.     }
  106.     if (i < 0)
  107.     {
  108.         for (i = NANONS; --i >= 0 ; )
  109.         {
  110.             if (anon[i].nblks > 0 && anon[i].tmpnum == num)
  111.             {
  112.                 break;
  113.             }
  114.         }
  115.     }
  116.  
  117.     /* if nobody else needs it, then discard the tmp file */
  118.     if (i < 0)
  119.     {
  120. #if MSDOS || TOS
  121.         strcpy(cutfname, o_directory);
  122.         if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i - 1]))
  123.             cutfname[i++] = SLASH;
  124.         sprintf(cutfname + i, TMPNAME + 3, getpid(), num);
  125. #else
  126.         sprintf(cutfname, TMPNAME, o_directory, getpid(), num);
  127. #endif
  128.         unlink(cutfname);
  129.     }
  130. }
  131.  
  132. /* This function frees a cut buffer.  If it was the last cut buffer that
  133.  * refered to an old temp file, then it will delete the temp file. */
  134. static void cutfree(buf)
  135.     struct cutbuf    *buf;
  136. {
  137.     int    num;
  138.  
  139.     /* return immediately if the buffer is already empty */
  140.     if (buf->nblks <= 0)
  141.     {
  142.         return;
  143.     }
  144.  
  145.     /* else free up stuff */
  146.     num = buf->tmpnum;
  147.     buf->nblks = 0;
  148. #ifdef DEBUG
  149.     if (!buf->phys)
  150.         msg("cutfree() tried to free a NULL buf->phys pointer.");
  151.     else
  152. #endif
  153.     free((char *)buf->phys);
  154.  
  155.     /* maybe delete the temp file */
  156.     maybezap(num);
  157. }
  158.  
  159. /* This function is called when we are about to abort a tmp file.
  160.  *
  161.  * To minimize the number of extra files lying around, only named cut buffers
  162.  * are preserved in a file switch; the anonymous buffers just go away.
  163.  */
  164. void cutswitch()
  165. {
  166.     int    i;
  167.  
  168.     /* mark the current temp file as being "obsolete", and close it.  */
  169.     storename((char *)0);
  170.     close(tmpfd);
  171.     tmpfd = -1;
  172.  
  173.     /* discard all anonymous cut buffers */
  174.     for (i = 0; i < NANONS; i++)
  175.     {
  176.         cutfree(&anon[i]);
  177.     }
  178.  
  179.     /* delete the temp file, if we don't really need it */
  180.     maybezap(tmpnum);
  181. }
  182.  
  183. /* This function should be called just before termination of vi */
  184. void cutend()
  185. {
  186.     int    i;
  187.  
  188.     /* free the anonymous buffers, if they aren't already free */
  189.     cutswitch();
  190.  
  191.     /* free all named cut buffers, since they might be forcing an older
  192.      * tmp file to be retained.
  193.      */
  194.     for (i = 0; i < 27; i++)
  195.     {
  196.         cutfree(&named[i]);
  197.     }
  198.  
  199.     /* delete the temp file */
  200.     maybezap(tmpnum);
  201. }
  202.  
  203.  
  204. /* This function is used to select the cut buffer to be used next */
  205. void cutname(name)
  206.     int    name;    /* a single character */
  207. {
  208.     cbname = name;
  209. }
  210.  
  211.  
  212.  
  213.  
  214. /* This function copies a selected segment of text to a cut buffer */
  215. void cut(from, to)
  216.     MARK    from;        /* start of text to cut */
  217.     MARK    to;        /* end of text to cut */
  218. {
  219.     int        first;    /* logical number of first block in cut */
  220.     int        last;    /* logical number of last block used in cut */
  221.     long        line;    /* a line number */
  222.     int        lnmode;    /* boolean: will this be a line-mode cut? */
  223.     MARK        delthru;/* end of text temporarily inserted for apnd */
  224.     REG struct cutbuf *cb;
  225.     REG long    l;
  226.     REG int        i;
  227.     REG char    *scan;
  228.     char        *blkc;
  229.  
  230.     /* detect whether this must be a line-mode cut or char-mode cut */
  231.     if (markidx(from) == 0 && markidx(to) == 0)
  232.         lnmode = TRUE;
  233.     else
  234.         lnmode = FALSE;
  235.  
  236.     /* by default, we don't "delthru" anything */
  237.     delthru = MARK_UNSET;
  238.  
  239.     /* handle the "doingdot" quirks */
  240.     if (doingdot)
  241.     {
  242.         if (!cbname)
  243.         {
  244.             cbname = dotcb;
  245.         }
  246.     }
  247.     else if (cbname != '.')
  248.     {
  249.         dotcb = cbname;
  250.     }
  251.  
  252.     /* decide which cut buffer to use */
  253.     if (!cbname)
  254.     {
  255.         /* free up the last anonymous cut buffer */
  256.         cutfree(&anon[NANONS - 1]);
  257.  
  258.         /* shift the anonymous cut buffers */
  259.         for (i = NANONS - 1; i > 0; i--)
  260.         {
  261.             anon[i] = anon[i - 1];
  262.         }
  263.  
  264.         /* use the first anonymous cut buffer */
  265.         cb = anon;
  266.         cb->nblks = 0;
  267.     }
  268.     else if (cbname >= 'a' && cbname <= 'z')
  269.     {
  270.         cb = &named[cbname - 'a'];
  271.         cutfree(cb);
  272.     }
  273. #ifndef CRUNCH
  274.     else if (cbname >= 'A' && cbname <= 'Z')
  275.     {
  276.         cb = &named[cbname - 'A'];
  277.         if (cb->nblks > 0)
  278.         {
  279.             /* resolve linemode/charmode differences */
  280.             if (!lnmode && cb->lnmode)
  281.             {
  282.                 from &= ~(BLKSIZE - 1);
  283.                 if (markidx(to) != 0 || to == from)
  284.                 {
  285.                     to = to + BLKSIZE - markidx(to);
  286.                 }
  287.                 lnmode = TRUE;
  288.             }
  289.  
  290.             /* insert the old cut-buffer before the new text */
  291.             mark[28] = to;
  292.             delthru = paste(from, FALSE, TRUE);
  293.             if (delthru == MARK_UNSET)
  294.             {
  295.                 return;
  296.             }
  297.             delthru++;
  298.             to = mark[28];
  299.         }
  300.         cutfree(cb);
  301.     }
  302. #endif /* not CRUNCH */
  303.     else if (cbname == '.')
  304.     {
  305.         cb = &named[26];
  306.         cutfree(cb);
  307.     }
  308.     else
  309.     {
  310.         msg("Invalid cut buffer name: \"%c", cbname);
  311.         dotcb = cbname = '\0';
  312.         return;
  313.     }
  314.     cbname = '\0';
  315.     cb->tmpnum = tmpnum;
  316.  
  317.     /* detect whether we're doing a line mode cut */
  318.     cb->lnmode = lnmode;
  319.  
  320.     /* ---------- */
  321.  
  322.     /* Reporting... */    
  323.     if (markidx(from) == 0 && markidx(to) == 0)
  324.     {
  325.         rptlines = markline(to) - markline(from);
  326.         rptlabel = "yanked";
  327.     }
  328.  
  329.     /* ---------- */
  330.  
  331.     /* make sure each block has a physical disk address */
  332.     blksync();
  333.  
  334.     /* find the first block in the cut */
  335.     line = markline(from);
  336.     for (first = 1; line > lnum[first]; first++)
  337.     {
  338.     }
  339.  
  340.     /* fetch text of the block containing that line */
  341.     blkc = scan = blkget(first)->c;
  342.  
  343.     /* find the mark in the block */
  344.     for (l = lnum[first - 1]; ++l < line; )
  345.     {
  346.         while (*scan++ != '\n')
  347.         {
  348.         }
  349.     }
  350.     scan += markidx(from);
  351.  
  352.     /* remember the offset of the start */
  353.     cb->start = scan - blkc;
  354.  
  355.     /* ---------- */
  356.  
  357.     /* find the last block in the cut */
  358.     line = markline(to);
  359.     for (last = first; line > lnum[last]; last++)
  360.     {
  361.     }
  362.  
  363.     /* fetch text of the block containing that line */
  364.     if (last != first)
  365.     {
  366.         blkc = scan = blkget(last)->c;
  367.     }
  368.     else
  369.     {
  370.         scan = blkc;
  371.     }
  372.  
  373.     /* find the mark in the block */
  374.     for (l = lnum[last - 1]; ++l < line; )
  375.     {
  376.         while (*scan++ != '\n')
  377.         {
  378.         }
  379.     }
  380.     if (markline(to) <= nlines)
  381.     {
  382.         scan += markidx(to);
  383.     }
  384.  
  385.     /* remember the o